<?php
include_once BXD_INCLUDEPATH . 'core/Action.php';
include_once BXD_INCLUDEPATH . 'core/Controller.php';
include_once BXD_INCLUDEPATH . 'core/Redirect.php';

include_once BXD_INCLUDEPATH . 'core/event/EventManager.php';
include_once BXD_INCLUDEPATH . 'core/event/EventListener.php';
include_once BXD_INCLUDEPATH . 'core/event/Event.php';

include_once BXD_INCLUDEPATH . 'core/plugin/BXDPlugin.php';
include_once BXD_INCLUDEPATH . 'core/plugin/BXDPluginManager.php';

$response = NULL;

/**
 * Core di BXDFramework
 * Qui avviene l'inizializzazione di tutte le funzioni principali necessarie al
 * corretto funzionamento del sistema
 *
 * @author Fabrizio Filieri
 */
class BXD
{
	private	static $instance	= null;

	private	$controller			= null;
	private	$action				= null;
	private	$content			= array();
	private	$layout				= null;
	private	$query				= null;
	private	$controllerClass	= null;
	private $params				= null;
	private $newRoute			= null;

	private function __construct()
	{
		BXDPluginManager::getInstance()->initAll();
	}

	/**
	 * Metodo per ricreare il pattern SINGLETON della classe principale del FRAMEWORK
	 *
	 * @return BXD Core dell'MVC
	 */

	public static function getInstance()
	{
		if (self::$instance == null)
		{
			self::$instance = new BXD();
		}

		return self::$instance;
	}

	public function run()
	{
		global $response, $bxd, $session, $post;

		$URL = trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/');

		EventManager::getInstance()->notify(Event::$ON_BEFORE_PARSING_URL, array('url' => $URL));

		$URL = (($this->newRoute !== NULL) ? $this->newRoute : trim(parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH), '/'));

		if($this->controller === NULL && $this->action === NULL)
		{
			if($URL != "")
			{
				$this->query = explode("/", $URL);
				$this->controller 	= $this->query[0];
				$this->action 		= $this->query[1];
			}
			else
			{
				$this->controller 	= BXD_CONTROLLER_DEFAULT;
				$this->action 		= BXD_ACTION_DEFAULT;
			}
		}

		EventManager::getInstance()->notify(Event::$ON_START, array('controller' => $this->controller, 'action' => $this->action, 'requestUrl' => $URL));

		if (file_exists(BXD_INCLUDEPATH_SITE . BXDConfig::$CONTROLLER_FOLDER . '/' . $this->controller . ".php"))
		{
			include_once BXD_INCLUDEPATH_SITE . BXDConfig::$CONTROLLER_FOLDER . '/' . $this->controller . '.php';

			$this->controllerClass = new $this->controller();

			if($this->params === NULL)
			{
				$this->params = array();

				for($i = 2; $i < count($this->query); $i++)
					$this->params[] = $this->query[$i];
			}

			// Verifico se esiste metodo specificato nell'url. Se si lo richiamo passando eventuali valori
			if (method_exists($this->controllerClass, $this->action))
			{
				EventManager::getInstance()->notify(Event::$ON_BEFORE_CALL_CONTROLLER_ACTION, array('controller'=>$this->controller, 'action'=>$this->action, 'param' => $this->params));

				try
				{
					$this->controllerClass->addContent($this->controller . "/" . $this->action);
					call_user_func_array(array($this->controllerClass, $this->action), $this->params);

					$this->layout	= $this->controllerClass->getLayout();

					$response = $this->controllerClass->getResponse();
					
				}
				catch(DatabaseConnectionErrorException $e)
				{
					$response = (($response === NULL) ? new Response() : $response);
					$response->message = $e->getMessage();

					$this->layout 	= BXD_LAYOUT_ERROR;
					$this->view 	= PAGE_CONNECT_DB_ERROR;
				}

				EventManager::getInstance()->notify(Event::$ON_AFTER_CALL_CONTROLLER_ACTION, array('controller'=>$this->controller, 'action'=>$this->action, 'param' => $this->params, 'layout'=> $this->layout));

				// esiste il controller e la action stampiamo il contenuto
				if (!isset($this->layout))
					$this->layout = BXD_LAYOUT_DEFAULT;

				EventManager::getInstance()->notify(Event::$ON_BEFORE_LOAD_LAYOUT, array('controller'=>$this->controller, 'action'=>$this->action, 'param' => $this->params, 'layout'=> $this->layout));
				include_once BXD_INCLUDEPATH_SITE . BXDConfig::$LAYOUT_FOLDER . '/' . $this->layout . '.php';
				EventManager::getInstance()->notify(Event::$ON_AFTER_LOAD_LAYOUT, array('controller'=>$this->controller, 'action'=>$this->action, 'param' => $this->params, 'layout'=> $this->layout));
			}
			else // il metodo della action non esiste
			{
				$this->addContent(BXD_PAGE_NOTFOUND);
				$this->layout = BXD_LAYOUT_ERROR;
				EventManager::getInstance()->notify(Event::$ON_UNKNOWN_ACTION, array('controller'=>$this->controller, 'action'=>$this->action));
				header("HTTP/1.0 404 Not Found");
				include_once BXD_INCLUDEPATH_SITE . BXDConfig::$LAYOUT_FOLDER . '/' . $this->layout . '.php';
			}

		}
		else // il controller non esiste
		{
			$this->addContent(BXD_PAGE_NOTFOUND);
			$this->layout = BXD_LAYOUT_ERROR;
			EventManager::getInstance()->notify(Event::$ON_UNKNOWN_CONTROLLER, array('controller'=>$this->controller, 'action'=>$this->action));
			header("HTTP/1.0 404 Not Found");
			include_once BXD_INCLUDEPATH_SITE . BXDConfig::$LAYOUT_FOLDER . '/' . $this->layout . '.php';
		}

		EventManager::getInstance()->notify(Event::$ON_CLOSE, array('controller'=>$this->controller, 'action'=>$this->action));
	}

	/**
	 * Metodo utilizzato per sollevare nuove eccezioni customizzate
	 *
	 * @param String $exception	Nome dell'eccezione che si intende sollevare
	 * @param String $message	Valore da attribuire all'eccezione
	 */

	public static function newException($exception, $message)
	{
		include_once BXD_INCLUDEPATH . 'lib/exception/' . $exception . 'Exception.php';
		$exceptionClass = $exception . "Exception";
		
		throw new $exceptionClass($message);
	}

	/**
	 * Restituisce il contenuto per il layout.
	 *
	 * @return string $response il contenuto dell'azione per il layout
	 */

	public function getContent($placemark = 'content')
	{
		global $response, $bxd, $session, $post;

		if(!isset($this->content[$placemark]))
			return;

		foreach ($this->content[$placemark] as $content)
		{
			if (file_exists(BXD_INCLUDEPATH_SITE . BXDConfig::$VIEW_FOLDER . '/' . $content . '.page.php'))
			{
				include(BXD_INCLUDEPATH_SITE . BXDConfig::$VIEW_FOLDER . '/' . $content . '.page.php');
			}
		}
	}

	/**
	 * Metodo che stampa il contenuto degli head specificati nel view con estensione '.head'
	 */
	public function getHead()
	{
		foreach ($this->content as $contents)
		{
			foreach ($contents as $content)
			{
				if (file_exists(BXD_INCLUDEPATH_SITE . BXDConfig::$VIEW_FOLDER . '/' . $content . '.head.php'))
				{
					include_once BXD_INCLUDEPATH_SITE . BXDConfig::$VIEW_FOLDER . '/' . $content . '.head.php';
				}
			}
		}
	}

	/**
	 * Ritorna il nome del controller attuale
	 *
	 * @return string il nome del controller attuale
	 */
	public function getController()
	{
		return $this->controller;
	}

	/**
	 * Imposta il nome del controller attuale. Questa modifica non ha effetto dopo che il controller è stato già caricato.
	 *
	 * @param string $controller il nome del controller da impostare
	 */
	public function setController($controller)
	{
		$this->controller = $controller;
	}

	/**
	 * Ritorna la action corrente.
	 *
	 * @return string la action corrente
	 */
	public function getAction()
	{
		return $this->action;
	}

	/**
	 * Imposta la action corrente. Questa operazione non ha effetto dopo che la action è stata invocata.
	 *
	 * @param string $action il nome della action da impostare
	 */
	public function setAction($action)
	{
		$this->action = $action;
	}

	/**
	 * Ritorna il nome del layout attualmente impostato.
	 *
	 * @return string il nome del layout impostato
	 */
	public function getLayout()
	{
		return $this->layout;
	}

	/**
	 * Imposta il nome del layout. Tale operazione non ha effetto se invocata dopo aver già caricato il layout.
	 * 
	 * @param string $layout il nome del layout da caricare
	 */
	public function setLayout($layout)
	{
		$this->layout = $layout;
	}

	/**
	 * Aggiunge un contenuto al placemark.
	 *
	 * @param string $content il contenuto da aggiungere
	 * @param string $placemark il placemark nel quale aggiunere il cotenuto
	 */
	public function addContent($content, $placemark='content')
	{
		if (!isset($this->content[$placemark]))
			$this->content[$placemark] = array();

		$this->content[$placemark][] = $content;
	}

	/**
	 * Ritorna true se la action di quel controller esiste, false se non esiste la action o non esiste
	 * il controller.
	 *
	 * @param <type> $controller il controller
	 * @param <type> $action la action
	 */
	public static function actionExists($controller, $action)
	{
		if (!file_exists(BXD_INCLUDEPATH_SITE . BXDConfig::$CONTROLLER_FOLDER . '/' . $controller . ".php"))
			return false;

		include_once BXD_INCLUDEPATH_SITE . BXDConfig::$CONTROLLER_FOLDER . '/' . $controller . '.php';
		$controllerClass = new $controller();
		if (!method_exists($controllerClass, $action))
			return false;

		return true;
	}

	
	/**
	 * Sostituisce il contenuto di un placemark con quello passato. Elimina tutti i contenuti
	 * precedentemente inseriti nel placemark. Viene usato anche per sostituire, ove esiste,
	 * il contenuto del placemark di default.
	 *
	 * @param string $content il nuovo contenuto del placemark
	 * @param string $placemark il placemark
	 */
	public function replaceContent($content, $placemark='content')
	{
		//var_dump($this->content[$placemark]);
		$this->content[$placemark] = array();

		$this->content[$placemark][] = $content;
		//var_dump($this->content[$placemark]);
	}

	/**
	 * Restituisce la query string corrispondente alla URL del sito
	 *
	 * @return string Query string
	 */

	public function getQuery()
	{
		return $this->query;
	}

	/**
	 * Imposta la query string corrispondente alla URL del sito
	 *
	 * @param String $query Query string
	 */

	public function setQuery($query)
	{
		$this->query = $query;
	}

	public function getParams()
	{
		return $this->params;
	}

	public function setParams($params)
	{
		$this->params = $params;
	}

	public function setNewRoute($newRoute)
	{
		$this->newRoute = $newRoute;
	}

	/**
	 * Include una classe dalla cartella di default delle classi.
	 *
	 * @param string $className il nome della classe da includere
	 */
	public static function loadClass($className)
	{
		include_once('../'.BXDConfig::$CLASS_FOLDER.'/'.$className.'.php');
	}

	/**
	 * Include un model o un entitymanager dalla cartella di default dei model.
	 *
	 * @param string $model il nome del model o dell'entity manager da includere
	 */
	public static function loadModel($model)
	{
		include_once('../'.BXDConfig::$MODEL_FOLDER.'/'.$model.'.php');
	}

	/**
	 * Include una classe dalla libreria del BXD
	 *
	 * @param string $className il nome della classe da includere
	 */
	public static function loadLib($className)
	{
		include_once(BXD_INCLUDEPATH."lib/".$className.'.php');
	}

	/**
	 * Include un plugin libreria del BXD
	 *
	 * @param string $className il nome della classe da includere
	 */
	public static function loadSystemPlugin($className)
	{
		include_once(BXD_INCLUDEPATH."plugin/".$className.'.php');
	}


	/**
	 * Include un plugin locale al progetto del BXD
	 *
	 * @param string $className il nome della classe da includere
	 */
	public static function loadPlugin($className)
	{
		include_once('../'.BXDConfig::$PLUGIN_FOLDER.'/'.$className.'.php');
	}




}

?>
